package com.coupon.service.impl;

import com.api.client.IdClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.constant.CouponStatus;
import com.common.constant.CouponType;
import com.common.constant.RedisKey;
import com.common.context.UserContext;
import com.common.domain.dto.CouponAddDTO;
import com.common.domain.dto.CouponIssueFormDTO;
import com.common.domain.dto.CouponListByConditionDTO;
import com.common.domain.dto.CouponUpdateDTO;
import com.common.domain.po.Coupon;
import com.common.domain.vo.CouponLIstByConditionVO;
import com.common.exception.CodeAndMsg;
import com.common.exception.JavaSmException;
import com.common.utils.BeanUtils;
import com.common.utils.Convert;
import com.coupon.mapper.CouponMapper;
import com.coupon.service.CouponService;
import com.coupon.task.ScheduledTasks;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * @author 路亚
 * @version 0.1
 * @className CouponServiceImpl
 * @description
 * @date 2024/12/21 15:04
 * @since jdk11
 */
@Service
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    private final CouponMapper couponMapper;

    private final StringRedisTemplate stringRedisTemplate;

    private final ScheduledTasks scheduledTasks;

    private final ThreadPoolTaskExecutor taskExecutor;

    private final IdClient idClient;

    public static final String COUPON_TYPE = "coupon";

    @Override
    public void add(CouponAddDTO couponAddDTO) {
        Coupon coupon = BeanUtils.copyBean(couponAddDTO, Coupon.class, (source, target) -> {
            target.setId(idClient.getLongKeyInterval(COUPON_TYPE));
            target.setStatus(CouponStatus.TO_BE_ISSUED);
            if (source.getType() == CouponType.OFFSET_COIL) {
                target.setDiscountValue(0);
            }
            target.setIssueNum(0);
            target.setUsedNum(0);
            target.setCreateTime(LocalDateTime.now());
            target.setUpdateTime(LocalDateTime.now());
            target.setCreater(UserContext.getCurrentId());
            target.setUpdater(UserContext.getCurrentId());
        });
        save(coupon);
    }

    @Override
    public void updateCouponById(CouponUpdateDTO couponUpdateDTO) {
        Coupon coupon = getById(couponUpdateDTO.getId());
        if (coupon == null) {
            throw new JavaSmException(CodeAndMsg.COUPON_NOT_EXIST);
        }
        if (coupon.getStatus() != CouponStatus.TO_BE_ISSUED) {
            throw new JavaSmException(CodeAndMsg.COUPON_STATUS_ERROR);
        }
        org.springframework.beans.BeanUtils.copyProperties(couponUpdateDTO, coupon);
        if (coupon.getType() == CouponType.OFFSET_COIL) {
            coupon.setDiscountValue(0);
        }
        coupon.setUpdateTime(LocalDateTime.now());
        coupon.setUpdater(UserContext.getCurrentId());
        updateById(coupon);
    }

    @Override
    public CouponLIstByConditionVO listByCondition(CouponListByConditionDTO couponListByConditionDTO, Integer page) {
        Page<Coupon> couponPage = new Page<>(page, 10);
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();
        if (couponListByConditionDTO.getType() != null) {
            queryWrapper.eq("type", couponListByConditionDTO.getType());
        }
        if (couponListByConditionDTO.getStatus() != null) {
            queryWrapper.eq("status", couponListByConditionDTO.getStatus());
        }
        if (!StringUtils.isEmpty(couponListByConditionDTO.getName())) {
            queryWrapper.like("name", couponListByConditionDTO.getName());
        }
        Page<Coupon> result = couponMapper.selectPage(couponPage, queryWrapper);
        return new CouponLIstByConditionVO(result.getTotal(), page, result.getRecords());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issueCoupon(CouponIssueFormDTO couponIssueFormDTO) {
        // 发放优惠券有两种方式：定时发放、立即发放
        // 1.1 立即发放后，需要修改 状态 为 发放中
        if (couponIssueFormDTO.getIsIssue()) {
            couponIssueFormDTO.setIssueBeginTime(LocalDateTime.now());
            updateStatusCoupon(couponIssueFormDTO);
        } else {
            // 2.1 定时发放
            scheduledTasks.scheduleTask(couponIssueFormDTO.getIssueBeginTime(), () -> {
                updateStatusCoupon(couponIssueFormDTO);
            });
        }
    }

    private void updateStatusCoupon(CouponIssueFormDTO couponIssueFormDTO) {
        Long id = couponIssueFormDTO.getId();
        Coupon coupon = getById(id);
        if (coupon == null) {
            throw new JavaSmException(CodeAndMsg.COUPON_NOT_EXIST);
        }
        org.springframework.beans.BeanUtils.copyProperties(couponIssueFormDTO, coupon);
        coupon.setStatus(CouponStatus.UNDER_WAY);
        coupon.setUpdater(UserContext.getCurrentId());
        updateById(coupon);
        taskExecutor.execute(() -> {
            // 1.2 添加到 redis 缓存:把发放时间范围、总数量、每人可领取的最大数量等信息都放到redis中，使用hash结构
            stringRedisTemplate.opsForHash().put(RedisKey.COUPON_TYPE + id, "totalNum", coupon.getTotalNum() + "");
            stringRedisTemplate.opsForHash().put(RedisKey.COUPON_TYPE + id, "userLimit", coupon.getUserLimit() + "");
            // 获取结束时间，把结束时间转换为距离计算机元年的毫秒值，存在到redis中
            stringRedisTemplate.opsForHash().put(RedisKey.COUPON_TYPE + id, "issueEndTime", coupon.getIssueEndTime().atZone(ZoneOffset.UTC).toInstant().toEpochMilli() + "");
        });
    }
}
